
#pragma once

#include "OgreMath/OgreVector3.h"
#include "OgreMath/OgreMatrix4.h"
#include "Projection.h"

class Camera
{
private:
	Ogre::Vector3 mPosition;
	Ogre::Vector3 mEyeDir;
	Ogre::Matrix4 mViewMatrix;

	Projection mProjection;

	/// Computes thew view matrix based on the camera position and direction;
	void computeViewMatrix()
	{
		Ogre::Matrix4 translationMatrix(Ogre::Matrix4::IDENTITY);
		translationMatrix.setTrans(-mPosition);

		Ogre::Vector3 up(0, 1, 0);
		Ogre::Vector3 horizontal = up.crossProduct(mEyeDir);
		Ogre::Vector3 vertical = mEyeDir.crossProduct(horizontal);

		Ogre::Matrix4 rotMatrix(horizontal.x, horizontal.y, horizontal.z, 0.0f, 
			vertical.x, vertical.y, vertical.z, 0.0f,
			mEyeDir.x, mEyeDir.y, mEyeDir.z, 0.0f,
			0.0f, 0.0f, 0.0f, 1.0f);

		mViewMatrix = rotMatrix * translationMatrix;	// first move, then rotate
	}

public:
	Camera(const Ogre::Vector3 &position, const Ogre::Vector3 &direction, const Projection &_projection) : mProjection(_projection)
	{
		setPositionAndDirection(position, direction);
	}

	/// Retrieves the view matrix for the camera.
	const Ogre::Matrix4& getViewMatrix() const { return mViewMatrix; }

	/// Sets the position and direction of the camera.
	void setPositionAndDirection(const Ogre::Vector3 &position, const Ogre::Vector3 &direction)
	{
		mPosition = position;
		mEyeDir = direction;
		computeViewMatrix();
	}

	/// Sets the camera position.
	void setPosition(const Ogre::Vector3 &position)
	{
		setPositionAndDirection(position, mEyeDir);
	}

	/// Sets the camera direction.
	void setDirection(const Ogre::Vector3 &direction)
	{
		setPositionAndDirection(mPosition, direction);
	}

	const Ogre::Vector3& getPosition() const { return mPosition; }
	const Ogre::Vector3& getDirection() const { return mEyeDir; }

	const Projection& getProjection() const { return mProjection; }

};